﻿using System;
using System.Collections.Generic;
using System.Text;
using AAA.Database;
using System.Data;
using System.Data.Common;
using AAA.DataAccess.Meta;
using AAA.Base.Util.Reader;

namespace AAA.DataAccess
{
    public abstract class AbstractDataAccess : IDataAccess
    {
        private DatabaseInfo _diDatabaseInfo;
        private string _strErrorMessage;

        private Dictionary<object, DataTable> _dicDataTable;
        private Dictionary<string, object> _dicParam;
        private Dictionary<string, string> _dicFieldHeader;

        private string _strConnectionString;

        public string ConnectionString
        {
            get { return _strConnectionString; }
            set { _strConnectionString = value; }
        }

        private const string DEFAULT_DATA_TABLE_NAME = "Default";

        public string ErrorMessage
        {
            get { return _strErrorMessage; }
            set { _strErrorMessage = value; }
        }

        private string _strConfigFile;

        protected string ConfigFile
        {
            get { return _strConfigFile; }
            set { _strConfigFile = value; }
        }

        private bool _needDecryption = false;

        public bool NeedDecryption
        {
            get { return _needDecryption; }
            set { _needDecryption = value; }
        }

        private IDatabase _database;

        public IDatabase Database
        {
            get 
            {
                if (_database == null)
                    _database = CreateDatabase();
                return _database; 
            }
            private set { _database = value; }
        }        

        protected abstract IDatabase CreateDatabase();

        public AbstractDataAccess()
        {
            _dicDataTable = new Dictionary<object, DataTable>();
            _dicParam = new Dictionary<string, object>();
            _dicFieldHeader = new Dictionary<string, string>();
        }

        #region IDataAccess 成員

        public DataTable GetData()
        {
            return GetData(DEFAULT_DATA_TABLE_NAME);
        }

        public DataTable GetData(object oName)
        {
            if (_dicDataTable.ContainsKey(oName) == false)
                return null;

            return _dicDataTable[oName];
        }

        public void AddData(DataTable dataTable)
        {
            AddData(DEFAULT_DATA_TABLE_NAME, dataTable);
        }

        public void AddData(object oName, DataTable dataTable)
        {
            oName = oName == null ? DEFAULT_DATA_TABLE_NAME : oName;

            if (_dicDataTable.ContainsKey(oName))
            {
                _dicDataTable[oName] = dataTable;
            }
            else
            {
                _dicDataTable.Add(oName, dataTable);
            }
        }

        public void AddData(DbDataReader dataReader)
        {
            AddData(DEFAULT_DATA_TABLE_NAME, dataReader);
        }

        public void AddData(object oName, DbDataReader dataReader)
        {
            oName = oName == null ? DEFAULT_DATA_TABLE_NAME : oName;

            DataTableX dataTable = new DataTableX();
            dataTable.Load(dataReader);
            AddData(oName, dataTable);
        }

        public void AddFieldHeader(string strFieldName, string strHeader)
        {
            if (_dicFieldHeader.ContainsKey(strFieldName))
                _dicFieldHeader[strFieldName] = strHeader;
            else
                _dicFieldHeader.Add(strFieldName, strHeader);
        }

        public string GetFieldHeader(string strFieldName)
        {
            return _dicFieldHeader.ContainsKey(strFieldName)
                    ? _dicFieldHeader[strFieldName]
                    : strFieldName;
        }

        public void AddParam(Dictionary<string, object> dicParam)
        {
            foreach (string strKey in dicParam.Keys)
                AddParam(strKey, dicParam[strKey]);
        }
        public void AddParam(string strName, object oValue)
        {
            if (_dicParam.ContainsKey(strName))
                _dicParam[strName] = oValue;
            else
                _dicParam.Add(strName, oValue);
        }

        public void RemoveParamByValue(List<object> lstValue)
        {
            List<string> lstRemoveKey = new List<string>();
            foreach (string strKey in _dicParam.Keys)
            {
                if (lstValue.Contains(_dicParam[strKey]) || lstValue.Contains("%" + _dicParam[strKey] + "%") || lstValue.Contains("%" + _dicParam[strKey]) || lstValue.Contains(_dicParam[strKey] + "%"))
                    lstRemoveKey.Add(strKey);
            }

            foreach (string strKey in lstRemoveKey)
                _dicParam.Remove(strKey);
        }

        public object GetParam(string strName)
        {
            return _dicParam.ContainsKey(strName)
                    ? _dicParam[strName]
                    : null;
        }

        public Dictionary<string, object> GetAllParam()
        {
            return _dicParam;
        }

        public void SetConfig(DatabaseInfo databaseInfo)
        {
            _diDatabaseInfo = databaseInfo;
        }

        public bool LoadConfig()
        {
            try
            {
                if (ConfigFile == null)
                {
                    ErrorMessage = "Please set config file at first!";
                    return false;
                }

                IniReader iniReader = ConfigFile.IndexOf(";") > -1 
                                        ? new IniReader(ConfigFile.Split(';')[0], ConfigFile.Split(';')[1]) 
                                        : new IniReader(ConfigFile);

                DatabaseInfo databaseInfo = new DatabaseInfo();
                databaseInfo.Host = iniReader.GetParam("Host");
                databaseInfo.Database = iniReader.GetParam("Database");
                databaseInfo.Username = iniReader.GetParam("Username");
                databaseInfo.Password = iniReader.GetParam("Password");
                SetConfig(databaseInfo);
                return true;
            }
            catch (Exception ex)
            {
                ErrorMessage = ex.Message + "\n" + ex.StackTrace;
            }
            return false;
        }

        public bool Connect()
        {
            if (Database == null)
            {
                Database = CreateDatabase();
//                ErrorMessage = "Please execute LoadConfig() at first!";
//                return false;
            }

            bool isSuccess = false;
            if(_diDatabaseInfo != null)
                if((isSuccess = _database.Open(_diDatabaseInfo.Host, 
                                               _diDatabaseInfo.Database, 
                                               _diDatabaseInfo.Username, 
                                               _diDatabaseInfo.Password)) == false)
                    ErrorMessage = _database.ErrorMessage;

            if (ConnectionString != null)
                if ((isSuccess = _database.Open(ConnectionString)) == false)
                    ErrorMessage = _database.ErrorMessage;

            return isSuccess;
        }

        public bool Disconnect()
        {
            try
            {
                if (Database != null)
                    Database.Close();
                return true;
            }
            catch (Exception ex)
            {
                ErrorMessage = ex.Message + "\n" + ex.StackTrace;
            }
            return false;
        }

        public bool Execute()
        {
            return Execute(null);
        }
        public bool Execute(object oDataSet)
        {
            if(_diDatabaseInfo == null)
                LoadConfig();

            ErrorMessage = null;
            Connect();
            if (BeforeExecute() == false)
            {
                Disconnect();
                return false;
            }

            if (ExecuteCommand(oDataSet) == false)
            {
                Disconnect();
                return false;
            }

            if (AfterExecute() == false)
            {
                Disconnect();
                return false;
            }
            Disconnect();
            return true;
        }

        public string ExecuteSQL
        {
            get
            {
                return _database != null
                        ? _database.ExecuteSQL
                        : null;
            }
        }

        public Dictionary<string, object> ExecuteParam
        {
            get
            {
                return _database != null
                        ? _database.ExecuteParam
                        : null;
            }
        }

        protected virtual bool NeedExecute(object oDataSet, object oDataName)
        {
            if ((!(oDataSet is int)) ||
                (!(oDataName is int)))
                return false;

            return (((int)oDataSet) & ((int)oDataName)) == ((int)oDataName); 
        }

        public virtual bool BeforeExecute()
        {
            return true;
        }

        public abstract bool ExecuteCommand(object oDataSet);

        public virtual bool AfterExecute()
        {
            return true;
        }

        #endregion
    }
}
